package projorientado.anpr.impl;

import projorientado.anpr.*;
import java.util.Vector;
import projorientado.image.Segment;
import projorientado.ocr.OCRecognizer;
import projorientado.ocr.RecognizedChar;

/**
 * Sistema de reconhecimento de caracteres em uma placa a partir de uma determinada formatação da placa.<BR>
 * A formatação definida será usada para encontrar o melhor caractere para cada segmento da placa.<BR>
 * A formatação deve ser em forma de String, em forma de uma expressão regular, onde cada posição define a formatação respectiva na placa.<BR>
 * Entre os blocos da placa deve-se usar um espado " " na formatação simbolizando o mesmo.<BR>
 * Os seguintes caractéres serão aceitos: '?' indifica um caractere alfa-numérico, 0 indifca um caractere númerico
 * e A indica uma letra.<BR>
 * Exemplo: "AAA 0000", será usado para uma placa que contém 3 letras no início (primeiro bloco) e 4 números no fim
 * (segunndo bloco).
 */
public class FormatedOCRPlate implements OCRPlate {
    
    private OCRecognizer ocr;
    private String format;
    private int maxSearch = -1;

    public FormatedOCRPlate(OCRecognizer ocr, String format) {
        this.ocr = ocr;
        this.format = format;
    }
    
    public void recognizePlate(Plate plate, int plateIndex, PlateRecognizerVisitor visitor) {
        
        Vector<Vector<Segment>> blocks = plate.getBlocks();
        Vector<Vector<RecognizedChar[]>> recognized = new Vector();
        
        float middle = 0;
        float maxCost = 0;
        
        int position = 0;
        StringBuilder ret = new StringBuilder();
        int count = 0;
        
        for(int b = 0; b < blocks.size(); b++) {
            Vector<Segment> segments = blocks.elementAt(b);
            Vector<RecognizedChar[]> rcs = new Vector();
            recognized.add(rcs);
            for(int i = 0; i < segments.size(); i++) {
                Segment segment = segments.elementAt(i);
                RecognizedChar[] rc = ocr.recognizeChar(segment.getImageLayer());
                rcs.add(rc);
                
                int p = findBest(rc,position++);
                
                if(p == -1) {
                    plate.setCode(Plate.PLATE_NOT_MATCH);
                    ret.append("?");
                } else {
                    count++;
                    ret.append(rc[p].getCharValue());
                    middle += rc[p].getCost();
                    if(rc[p].getCost() > maxCost)
                        maxCost = rc[p].getCost();
                    
                }
            }
            position++;
            ret.append(" ");
        }
        
        plate.setPlateString(ret.toString());
        plate.setMiddleCost(middle/count);
        plate.setMaxCost(maxCost);
    }
    
    /**
     * Procura o melhor caractere para uma determinda posição, analisando a formatação para posição.
     * @param rc vetor de caracteres reconhecidos e seus custos
     * @param position posição na placa/formatação
     * @return posição do vetor de entrada com melhor opção
     */
    private int findBest(RecognizedChar[] rc, int position) {
        char formatation = format.charAt(position);
        
        if(formatation == '?') {
            return 0;
        }
        
        int max = rc.length;
        if(maxSearch > 0) {
            max = Math.min(max, maxSearch);
        }
        
        if(Character.isDigit(formatation)) {
            for(int i = 0; i < max; i++) {
                if(Character.isDigit(rc[i].getCharValue())) {
                    return i;
                }
            }
            return -1;
        }
        
        if(Character.isLetter(formatation)) {
            for(int i = 0; i < max; i++) {
                if(Character.isLetter(rc[i].getCharValue())) {
                    return i;
                }
            }
            return -1;
        }
        return -1;
    }

}
